######################################################################
# Time-stamp: <05/06/16 11:32:49 ostolop>
#
# Package: EP::Core::Data::Parser
#
# The abstract Data::Parser package from which all parsers should inherit.
######################################################################
package EP::Core::Data::Parser;

use strict;

#use EP::Config;

use EP::Common;
use EP::Common::General;
use EP::Common::Data;
use EP::Common::DB;
use File::Temp;

# sub: new
#   The base constructor method for EP::Core::Data::Parser.
#
# parameters:
#   query      - the requesting CGI query
#   caller_epc - the caller EPC instance
sub new {
  my $proto = shift;
  my $class = ref ( $proto ) || $proto;
  my $self = {};

  my %params = @_;
  die "Trying to instantiate a parser without a data query in the parameters." if not $params{query};

  $self->{query} = $params{query};
  $self->{epc}   = $params{caller_epc};

  bless $self, $class;
  return $self;
}

# sub: parse_data
#   The overriding method should implement a mechanism to parse the data contained in the query.
#
# parameters:
#   TBD.
#
# returns:
#   An instance of (a subclass of) <EP::Common::DBI::Dataset>
sub parse_data {
  die "Base method called.";
}

# sub: upload_data
# uploads data from the query
sub upload_data {
  my $self = shift;

  $self->{epc}->{session}->status ( "Uploading the data..." );

  my $data;

  if ( $self->{query}->param ( "src_file" ) ) {
    $data = $self->_upload_file ();
    $data->{dataset_name} = $self->{query}->param ( "dataset_name" ) || $self->{query}->param ( "src_file" );
  }
  elsif ( $self->{query}->param ( "src_pubdb" ) ) {
    $data = $self->_upload_pubdb ();
    $data->{dataset_name} = $self->{query}->param ( "dataset_name" ) || $self->{query}->param ( "src_pubdb" );
  }
  elsif ( $self->{query}->param ( "src_url" ) ) {
    $data = $self->_upload_url ();
    $data->{dataset_name} = $self->{query}->param ("dataset_name") || "URL Upload";
  }
  else {
    $data = $self->_upload_data ();
    $data->{dataset_name} = $self->{query}->param ("dataset_name") || "Manual Entry";
  }

  return $data;
}

# sub: _upload_file
# parses submitted files
sub _upload_file {
  my $self = shift;
  my $data;

  if (1 == $self->{query}->param('local_mode')) {
    # in local mode files already created in temporary folder... so we just need to pass along the path
    $data->{data}         = $self->{query}->param('src_file')                 if $self->{query}->param('src_file');
    $data->{row_annot}    = $self->{query}->param('src_file_row_annot')       if $self->{query}->param('src_file_row_annot');
    $data->{column_annot} = $self->{query}->param('src_file_column_annot')    if $self->{query}->param('src_file_column_annot');
  } else {
    $data->{data}         = $self->_get_fh ( $self->{query}->param('src_file') )              if $self->{query}->param('src_file');
    $data->{row_annot}    = $self->_get_fh ( $self->{query}->param('src_file_row_annot') )    if $self->{query}->param('src_file_row_annot');
    $data->{column_annot} = $self->_get_fh ( $self->{query}->param('src_file_column_annot') ) if $self->{query}->param('src_file_column_annot');
  }
  
  return $data;
}

# sub: _upload_pubdb
# parses submitted files
sub _upload_pubdb {
  my $self = shift;
  my ($data);

  my ($pubdb) = $EP::Config::h->{EPNG_PUBDATA_PATH} . "/" .
    $self->{query}->param('dataset_type') . "/" . $self->{query}->param('src_pubdb');

  $data->{data}         = $pubdb;
  $data->{row_annot}    = "$pubdb.row_annot"    if -f "$pubdb.row_annot";
  $data->{column_annot} = "$pubdb.column_annot" if -f "$pubdb.column_annot";

  return $data;
}

# sub: _get_fh
# read data from an incoming filehandle into a temporary file and return the filehandle
sub _get_fh {
  my $self = shift;
  my ($fh) = @_;

  my $suf = "$fh";
  $suf =~ tr/a-zA-Z0-9.//cd;

  my $buf;
  my $tmp = new File::Temp ( DIR => "$EP::Config::h->{TMP}", SUFFIX => ".$suf", UNLINK => 0 );

  print $tmp $buf while sysread $fh, $buf, 100000;
  close $tmp;

  unlink $fh;
  return $tmp;
}

# sub: _upload_data
# parses data submitted as a 'cut-n-paste' operation
sub _upload_data {
  my $self = shift;

  my $src          = $self->{query}->param ( 'src' );
  my $row_annot    = $self->{query}->param ( 'src_row_annot' );
  my $column_annot = $self->{query}->param ( 'src_column_annot' );

  my $tmp_data      = new File::Temp ( DIR => "$EP::Config::h->{TMP}", UNLINK => 0 );
  my $tmp_row_annot = new File::Temp ( DIR => "$EP::Config::h->{TMP}", UNLINK => 0 ) if $row_annot;
  my $tmp_col_annot = new File::Temp ( DIR => "$EP::Config::h->{TMP}", UNLINK => 0 ) if $column_annot;

  print $tmp_data $src;
  print $tmp_row_annot $row_annot    if $row_annot;
  print $tmp_col_annot $column_annot if $column_annot;

  close $tmp_data;
  close $tmp_row_annot if $row_annot;
  close $tmp_col_annot if $column_annot;

  my $data = { data         => $tmp_data,
	       row_annot    => $tmp_row_annot,
	       column_annot => $tmp_col_annot
	     };

  return $data;
}

# sub: _upload_url
# parses submitted urls
sub _upload_url {
  my $self = shift;
  my ($data);

  $data->{data}         = $self->_get_url ( $self->{query}->param('src_url') )              if $self->{query}->param('src_url');
  $data->{row_annot}    = $self->_get_url ( $self->{query}->param('src_url_row_annot') )    if $self->{query}->param('src_url_row_annot');
  $data->{column_annot} = $self->_get_url ( $self->{query}->param('src_url_column_annot') ) if $self->{query}->param('src_url_column_annot');

  return $data;
}

# sub: _get_url
# reads data from a url and writes it to a temp file, returning the filehandle
sub _get_url {
  my $self = shift;
  my ($url) = @_;

  return if !$url;

  # set URL invalid only if it contains unprintable characters
  throw EP::Common::Error ( -value => CUD_INVALID_DATA_URL, -err_args => [$url] ) if ( $url =~ /[[:^print:]]/s );

  my ( $url_file_ext ) = $url =~ /\/.*\.(.*)$/;
  $url_file_ext = "tgz" if $url =~ /\.tar\.gz$/;
  warn "Got $url_file_ext";

  my $tmp = new File::Temp ( DIR => "$EP::Config::h->{TMP}", UNLINK => 0, SUFFIX => ".$url_file_ext" );

  my $cmd = "$EP::Config::h->{GET_CMD} '$url' > $tmp";
  my $out = system_call_with_output ( $cmd );

  die "Failed to retrieve data from $url: $out!" if defined EP::Common::Error->prior() and EP::Common::Error->prior()->value() == SYS_CALL_WITH_NONZERO_STATUS;

  close $tmp;
  return $tmp;
}

1;
